package com.ruoyi.datastatistics.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.basedata.domain.Concentrator;
import com.ruoyi.basedata.domain.RedisKeyName;
import com.ruoyi.basedata.domain.TerminalsXb;
import com.ruoyi.basedata.domain.vo.ConcentratorProtocol;
import com.ruoyi.basedata.domain.vo.TerminalsXbProtocol;
import com.ruoyi.basedata.service.IConcentratorService;
import com.ruoyi.basedata.service.ITerminalsXbService;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.CommonHelper;
import com.ruoyi.datastatistics.domain.MeterHistoryData;
import com.ruoyi.datastatistics.domain.Realtimegatherdata;
import com.ruoyi.datastatistics.domain.RealtimegatherdataXb;
import com.ruoyi.datastatistics.domain.parsevo.PacketForRedis;
import com.ruoyi.datastatistics.service.IMeterHistoryDataService;
import com.ruoyi.datastatistics.service.IRealtimegatherdataXbService;
import com.ruoyi.datastatistics.service.ISiLiancallBackService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Service
public class SiLiancallBackServiceImpl implements ISiLiancallBackService {
    @Autowired
    private RedisCache redisCache;
    /*   @Autowired
       private CommonHelper commonHelper;*/
    @Autowired
    private ITerminalsXbService terminalsXbService;

    @Autowired
    private IConcentratorService concentratorService;
    @Autowired
    private IRealtimegatherdataXbService realtimegatherdataXbService;
    @Autowired
    private IMeterHistoryDataService meterHistoryDataService;
    private static String factoryCode = "1014";

    @Override
    public void callBackSubscribe(String requestData) {

        if (!"".equals(requestData)) {
            PacketForRedis pr = nbIotpacketSaveTOJson(requestData);
            if (pr != null && !pr.getJsonData().equals("")) {
                parsingMain(pr);
            }
        }


    }

    @Override
    public void insertCallBack(List<MeterHistoryData> list, TerminalsXbProtocol terminalsXb) {
        try {
            if (list.size() > 0) {
                try {
                    //历史数据插入到redis中
                    meterHistoryDataToRedisMain(list, terminalsXb);
                    //log.Info(terminalAdd+"历史数据插入到redis中");
                } catch (Exception e) {
                    log.error("历史数据插入到redis中异常" + e.getMessage());
                }
                try {
                    //更新设备、包括redis、数据库  去掉，数据库中表档案无需更新
                    synchronousMostValueFromRealtimegatherdata(list, terminalsXb);
                    //log.Info(terminalAdd+"更新设备、包括redis、数据库");
                } catch (Exception e) {
                    log.error("更新设备、包括redis、数据库异常" + e.getMessage());
                }
                try {
                    //将历史数据批量插入数据库
                    InsertMeterHistoryData(list, terminalsXb);
                    //log.Info(terminalAdd+"历史数据批量插入数据库");
                } catch (Exception e) {
                    log.error("历史数据批量插入数据库异常" + e.getMessage());
                }
                try {
                    removeRealtimeDataInRedis(terminalsXb);
                } catch (Exception e) {
                    log.error("removeRealtimeDataInRedis异常" + e.getMessage());
                }
            }


        } catch (Exception e) {
        }

    }

    public void meterHistoryDataToRedisMain(List<MeterHistoryData> list, TerminalsXbProtocol terminalsXb) throws ParseException {
        for (MeterHistoryData l : list) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Boolean flag2 = null;
            try {
                flag2 = checkIsSaveInRedis(terminalsXb.getCommunicateAddress(), sdf.parse(l.getReadDate()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            if (!flag2) {

                //插入到redis中
                redisCache.SortedSetAdd(RedisKeyName.backupDB_HistoricalData + factoryCode+"_" + terminalsXb.getCommunicateAddress(), l, ConvertDateTimeToInt(sdf.parse(l.getReadDate())));
            } else {
                list.remove(l);
            }
        }


    }

    public void parsingMain(PacketForRedis packetForRedis) {
        try {
            String packetType = packetForRedis.packetType;
            JSONArray json = new JSONArray();
            String terminalAdd = packetForRedis.getTerminalId();
            TerminalsXbProtocol terminalsXb = redisCache.getCacheMapValue(RedisKeyName.terminalInfo + factoryCode, terminalAdd);
            //跟据数据类型进行业务处理
            List<MeterHistoryData> list = new ArrayList<MeterHistoryData>();
            try {
                //历史数据插入到redis中
                meterHistoryDataToRedis(list, packetForRedis, terminalsXb);
                //log.Info(terminalAdd+"历史数据插入到redis中");
            } catch (Exception e) {
                log.error(packetForRedis.imei + "历史数据插入到redis中异常" + e.getMessage());
            }
            try {
                //将历史数据批量插入数据库
                InsertMeterHistoryData(list, terminalsXb);
                //log.Info(terminalAdd+"历史数据批量插入数据库");
            } catch (Exception e) {
                log.error(packetForRedis.imei + "历史数据批量插入数据库异常" + e.getMessage());
            }

        } catch (Exception e) {
        }

    }

    //历史数据插入到redis中
    public void meterHistoryDataToRedis(List<MeterHistoryData> list, PacketForRedis packetForRedis, TerminalsXbProtocol terminalsXb) {
        String errorflag = "200";
        try {
            JSONObject packet = packetForRedis.getJsonData();
            BigDecimal CumulantiveRate = new BigDecimal(1);
            errorflag = joiningTerminalpackage(list, packet, CumulantiveRate);
            packetForRedis.actionStatus = errorflag;
            packetForRedis.insertime = ConvertDateTimeToInt(new Date());
            // redis.ListRightPush(RedisKeyName.InsertOver+ realtimeDetailDataForRedis.TerminalAddress, packetForRedis);
        } catch (Exception e) {
            errorflag = "508";//未知bug
            log.error(e.getMessage() + packetForRedis.toString());
        }
    }

    //历史数据插入到redis中
    public String joiningTerminalpackage(List<MeterHistoryData> list, JSONObject json, BigDecimal CumulantiveRate) throws ParseException {
        Boolean isAddRow = true;
        TerminalsXbProtocol terminalsXb = new TerminalsXbProtocol();

        String flag = "200";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        //RedisHelp.RedisHelper redis = new RedisHelp.RedisHelper(1);
        if (json != null) {
            Date prevTime = null;
            BigDecimal prevTotalFlow = null;
            String date = json.containsKey("date") ? "" : json.getString("date");
            String type = json.getString("type");
            JSONArray info = json.getJSONArray("info");


            for (int j = 0; j < info.size(); j++) {
                JSONObject job = info.getJSONObject(j);
                String add = job.getString("add");
                if (!add.equals("") && add != null) {
                    TerminalsXb terminalsDb_ = new TerminalsXb();
                    terminalsDb_.setTerminalId(add);
                    List<TerminalsXbProtocol> list_TerminalsXb = terminalsXbService.selectTerminalsXbProtocolList(terminalsDb_);
                    if (list_TerminalsXb != null && list_TerminalsXb.size() > 0) {
                        terminalsXb = list_TerminalsXb.get(0);
                    }
                } else {
                    return null;
                }
                String totalFlow = job.getString("TotalFlow");
                String valveStatus = job.getString("valveStatus");//阀门状态
                Date TerminalTime = null;
                try {
                    if (date == null || date.equals("")) {
                        TerminalTime = new Date();
                    } else {
                        TerminalTime = sdf.parse(date);
                    }

                } catch (Exception e) {
                    flag = "501";
                    log.error(terminalsXb.getTerminalId() + "@@TerminalTime:" + e.getMessage());
                    continue;
                }
                MeterHistoryData meterHistoryData = new MeterHistoryData();
                try {
                    isAddRow = true;
                    BigDecimal TotalFlow = null;
                    BigDecimal ReadNumber = null;
                    BigDecimal ReverseNumber = null;
                    BigDecimal InstantNumber = null;
                    BigDecimal Pressure = null;
                    BigDecimal InstantVelocity = null;

                    BigDecimal TerminalVol = null;
                    Integer TerminalCSQ = null;
                    try {
                        try {
                            meterHistoryData.setTotalFlow(job.get("TotalFlow") != null ? new BigDecimal(job.getString("TotalFlow")) : TotalFlow);
                            meterHistoryData.setReadNumber(job.get("ReadNumber") != null ? new BigDecimal(job.getString("ReadNumber")) : ReadNumber);
                            meterHistoryData.setReverseNumber(job.get("ReverseNumber") != null ? new BigDecimal(job.getString("ReverseNumber")) : ReverseNumber);
                        } catch (Exception e) {
                            log.error(terminalsXb.getTerminalId() + "@@流量解析异常" + e.getMessage());
                        }

                        sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                        meterHistoryData.setTenantId(terminalsXb.getTenantId());
                        meterHistoryData.setSupplierId(factoryCode);
                        meterHistoryData.setTerminalId(terminalsXb.getTerminalId());
                        meterHistoryData.setLocationNb(terminalsXb.getLocationNb());
                        meterHistoryData.setTerminalVol(job.get("voltage") != null ? new BigDecimal(job.getString("voltage")) : TerminalVol);
                        meterHistoryData.setTerminalCsq(TerminalCSQ);
                        meterHistoryData.setValveState(new Integer(valveStatus));
                        meterHistoryData.setReadDate(sdf.format(TerminalTime));
                        meterHistoryData.setCreateDate(new Date());
                        Boolean flag2 = checkIsSaveInRedis(terminalsXb.getCommunicateAddress(), TerminalTime);
                        if (!flag2) {
                            list.add(meterHistoryData);
                            //插入到redis中
                            redisCache.SortedSetAdd(RedisKeyName.backupDB_HistoricalData + factoryCode+"_" + terminalsXb.getCommunicateAddress(), meterHistoryData, ConvertDateTimeToInt(TerminalTime));
                        }
                    } catch (Exception e) {
                        flag = "502";
                        log.error(terminalsXb.getTerminalId() + "@@502" + e.getMessage());
                    }


                } catch (Exception e) {
                    flag = "506";
                    log.error(terminalsXb.getTerminalId() + "joiningTerminalpackage抛出异常：" + e.getMessage());
                }

                try {
                    //更新设备、包括redis、数据库  去掉，数据库中表档案无需更新
                    synchronousMostValueFromRealtimegatherdata(list, terminalsXb);
                    //log.Info(terminalAdd+"更新设备、包括redis、数据库");
                } catch (Exception e) {
                    log.error(terminalsXb.getTerminalId() + "更新设备、包括redis、数据库异常" + e.getMessage());
                }

                try {
                    removeRealtimeDataInRedis(terminalsXb);
                } catch (Exception e) {
                    log.error(terminalsXb.getTerminalId() + "removeRealtimeDataInRedis异常" + e.getMessage());
                }
            }

        }
        return flag;
    }

    /**
     * 更新实时数据表
     *
     * @param list
     * @param terminalsXb
     */
    public void synchronousMostValueFromRealtimegatherdata(List<MeterHistoryData> list, TerminalsXbProtocol terminalsXb) throws ParseException {

        if (list.size() > 0) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            RealtimegatherdataXb realtimegatherdata = new RealtimegatherdataXb();
//            realtimegatherdata.setRoomId(terminalsXb.getRoomId());
            realtimegatherdata.setLocationNb(terminalsXb.getLocationNb());
            List<RealtimegatherdataXb> isupate = realtimegatherdataXbService.selectRealtimegatherdataXbList(realtimegatherdata);

            MeterHistoryData t = list.get(list.size() - 1);
            realtimegatherdata.setTerminalId(t.getTerminalId());
            realtimegatherdata.setLocationNb(t.getLocationNb());
            realtimegatherdata.setTenantId(t.getTenantId());
            realtimegatherdata.setTotalFlow(t.getTotalFlow());
            realtimegatherdata.setTerminalCsq(t.getTerminalCsq());
            realtimegatherdata.setTerminalVol(t.getTerminalVol());
            if (t.getValveState() != null) {
                realtimegatherdata.setValveState(new BigDecimal(t.getValveState()));
            }
            realtimegatherdata.setReadDate(sdf.parse(t.getReadDate()));
            realtimegatherdata.setCreateTime(new Date());

            if (isupate.size() > 0) {
                //更新
                realtimegatherdataXbService.updateRealtimegatherdataXb(realtimegatherdata);
            } else {
                //插入
                realtimegatherdataXbService.insertRealtimegatherdataXb(realtimegatherdata);
            }
        }
    }

    /**
     * 时间添加
     *
     * @param date
     * @param minutes
     * @return
     */
    public static Date addMinutes(Date date, int minutes) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MINUTE, minutes);
        return calendar.getTime();
    }

    /// <summary>
    /// 是否再redis中存储，false没有存储，true已经存储
    /// </summary>
    /// <param name="terminaladd"></param>
    /// <param name="stratTime"></param>
    /// <returns></returns>
    public Boolean checkIsSaveInRedis(String terminaladd, Date stratTime) {
        Boolean flag = false;

        Set<Realtimegatherdata> list = redisCache.SortedSetRangeByRank(RedisKeyName.backupDB_HistoricalData + factoryCode+"_" + terminaladd, ConvertDateTimeToInt(stratTime), ConvertDateTimeToInt(stratTime));
        if (null != list && list.size() > 0) {
            flag = true;
        }
        return flag;
    }

    /**
     * 时间处理
     *
     * @param date
     * @return
     * @throws ParseException
     */
    public String getPackageDate(String date) throws ParseException {
        String date_ = "";//时间 临时
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        if (!date.equals(""))
            date_ = date.length() > 4 ? sdf.format(new Date()).substring(0, 2) + date : sdf.format(new Date()) + date;
        sdf = new SimpleDateFormat("yyyyMMdd");
        Date dt = sdf.parse(date_);

        if (dt.compareTo(new Date()) > 0) {
            String year = new Date().getYear() - 1 + "";
            date = date.length() > 4 ? date_ : year + date;

        } else {
            date = date_;
        }
        return date;
    }

    /**
     * 时间处理
     *
     * @param time
     * @param date
     * @return
     */
    public String getPackageTime(String time, String date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        time = time.length() < 2 ? "0" + time : time;
        time = time.length() < 4 ? (date + time + "0000") : sdf.format(new Date()).substring(0, 2) + time + "00";
        return time;
    }

    /// <summary>
    /// 插入日冻结数据
    /// </summary>
    /// <param name="dt"></param>
    /// <param name="con"></param>
    /// <returns></returns>
    public boolean InsertMeterHistoryData(List<MeterHistoryData> list, TerminalsXb terminalsXb) {
        boolean copy = false;
        if (list.size() > 0) {
            meterHistoryDataService.batchinsertMeterHistoryData(list);
            //realtimegatherdataXbService.batchinsertRealtimegatherdataXb(list);
            copy = true;
        }

        return copy;
    }

    /// <summary>
    /// 清理redis中数据
    /// </summary>
    /// <param name="terminalAdd"></param>
    public void removeRealtimeDataInRedis(TerminalsXb terminalsXb) {
        try {
            Set<Realtimegatherdata> firt_list = redisCache.SortedSetRangeByRank(RedisKeyName.backupDB_HistoricalData+factoryCode+"_" + terminalsXb.getCommunicateAddress(), 0, 0);

            if (firt_list != null && firt_list.size() > 0) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Long interval = new Date().getTime() - sdf.parse(firt_list.stream().findFirst().get().getTerminalTime()).getTime();
                if (interval / (24 * 60 * 60 * 1000) > RedisKeyName.backupDB_HistoricalData_days) {
                    long st = new Date().getTime() - RedisKeyName.backupDB_HistoricalData_days * (24 * 60 * 60 * 1000);
                    redisCache.SortedSetRemoveByScore(RedisKeyName.backupDB_HistoricalData+factoryCode+"_" + terminalsXb.getCommunicateAddress(), sdf.parse(firt_list.stream().findFirst().get().getTerminalTime()).getTime(), st);

                }
            }


        } catch (Exception e) {
            log.error(terminalsXb.getTerminalId() + "历史数据清理异常" + e.getMessage());
        }
    }

    /// <summary>
    /// 将NB传过来的值解析成json存储到redis中
    /// </summary>
    /// <param name="data"></param>
    /// <param name="terminalAdd"></param>
    public PacketForRedis nbIotpacketSaveTOJson(String requestData) {
        String terminalAdd = "";
        PacketForRedis packetForRedis = new PacketForRedis();
        try {
            JSONObject jo = JSONObject.parseObject(requestData);
            if (jo.get("upDataList") != null) {
                byte[] data = null;
                String data_ = jo.getJSONArray("upDataList").getJSONObject(0).getString("upData");
                data = hexStringToByteArray(data_);
                // log.info(data_);
                // terminalAdd = getTerminalAdd(data);//截取地址
                String imei = jo.getString("imei");
                //获取设备基础信息，获取属性协议
                packetForRedis = getPacketForRedis(data, imei, imei, requestData);
                // redisCache.lPush(RedisKeyName.WaitingPacketInsert + terminalAdd, packetForRedis);
            }

        } catch (Exception e) {
            redisCache.lPush(RedisKeyName.exceptions + factoryCode + "_" + terminalAdd, requestData);//记录异常
            redisCache.trim(RedisKeyName.exceptions + factoryCode + "_" + terminalAdd, -4, -1);//删除exceptions数
        }
        return packetForRedis;
    }

    /**
     * @param data
     * @param terminalAdd 与 imei 相同
     * @param imei
     * @param requestData
     * @return
     */
    private PacketForRedis getPacketForRedis(byte[] data, String terminalAdd, String imei, String requestData) {
        String aClassName = "";
        Concentrator concentrator = new ConcentratorProtocol();


        try {
            concentrator.setConcentratorId(terminalAdd);
            concentrator.setSupplierId(factoryCode);//selectTerminalsDbProtocolList
            ConcentratorProtocol concentratorProtocol = concentratorService.selectConcentratorProtocolList(concentrator).get(0);
            aClassName = concentratorProtocol.getClassName();
        } catch (Exception e) {
            log.info(terminalAdd + ",没有找到此集中器档案");
        }

        if ("".equals(aClassName) || null == aClassName) //aClassName = "com.ruoyi.datastatistics.service.impl.DeGaocallBackServiceImpl";
            aClassName = "com.ruoyi.datastatistics.Agreement.GuoXinDTUV1";
        //解析成json,存储到redis
        Object param = data;
        JSONObject temp = getPacketForRedis(data, aClassName);

        // JObject json_packet = JObject.FromObject(temp);
        int packetType = data[5];

        PacketForRedis packetForRedis = new PacketForRedis();
        packetForRedis.setImei(imei);
        packetForRedis.setPacketType(packetType + "");
        packetForRedis.setJsonData(temp);
        packetForRedis.setNettycreatetime(ConvertDateTimeToInt(new Date()));
        packetForRedis.setTerminalId(terminalAdd);
        return packetForRedis;
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

    private JSONObject getPacketForRedis(byte[] data, String aClassName) {
        JSONObject jo = new JSONObject();
        JSONObject jsonObject = new JSONObject();
        Object[] param = {data, 0, jsonObject};
//        if ((byte) data[4] == 0x02 || (byte) data[4] == 0x12) {
            Object temp = CommonHelper.getMethod(aClassName, "getDSDateinfoFromByte", param);
            jo = (JSONObject) temp;

//        }
        return jo;
    }

    /**
     * DateTime时间格式转换为Unix时间戳格式
     *
     * @param time
     * @return
     */
    public long ConvertDateTimeToInt(Date time) {
        long timeInMillis = time.getTime();
        long unixTime = timeInMillis / 1000;
        return unixTime;
    }

    private byte[] hexStringToByte(String hex) {
        int len = (hex.length() / 2); //除以2是因为十六进制比如a1使用两个字符代表一个byte
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            //乘以2是因为十六进制比如a1使用两个字符代表一个byte,pos代表的是数组的位置
            //第一个16进制数的起始位置是0第二个是2以此类推
            int pos = i * 2;
            //<<4位就是乘以16  比如说十六进制的"11",在这里也就是1*16|1,而其中的"|"或运算就相当于十进制中的加法运算
            //如00010000|00000001结果就是00010001 而00010000就有点类似于十进制中10而00000001相当于十进制中的1，与是其中的或运算就相当于是10+1(此处说法可能不太对，)
            result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }
        return result;
    }

    private byte toByte(char c) {
        byte b = (byte) "0123456789ABCDEF".indexOf(c);
        return b;
    }

    /**
     * 获取设备地址
     *
     * @param bit
     * @return
     */
    private String getTerminalAdd(byte[] bit) {
        byte[] bit_add = new byte[4];
        System.arraycopy(bit, 5, bit_add, 0, 4);
        int temp = Integer.parseInt(getIntFromByte2(bit_add));//终端地址
        return temp + "";
    }

    /**
     * 两位不足 补零,高位在hou,eg->0x0c 0x02:0c02
     *
     * @param bit
     * @return
     */
    public static String getIntFromByte2(byte[] bit) {
        String str = "";
        for (int i = 0; i < bit.length; i++) {
            String temp = Integer.toHexString(bit[i] & 0xFF);
            temp = temp.length() < 2 ? ("0" + temp) : temp;
            str = str + temp;
        }
        return str;
    }

}
